
#include "cl_base_BinaryClassification.h"

int CDataSetForBinaryClassification::GetExampleAmount()
{
	return (pRealDataSet==NULL ? 0 : pRealDataSet->GetExampleAmount());
}

double CDataSetForBinaryClassification::GetExampleInitialWeight(int ExampleIndex)
{
	return (pRealDataSet==NULL ? 1.0 : pRealDataSet->GetExampleInitialWeight(ExampleIndex));
}

int CDataSetForBinaryClassification::GetFeatureDimension()
{
	return (pRealDataSet==NULL ? 0 : pRealDataSet->GetFeatureDimension());
}

double CDataSetForBinaryClassification::GetExampleFeature(int ExampleIndex, int FeatureIndex)
{
	return (pRealDataSet==NULL ? 0.0 : pRealDataSet->GetExampleFeature(ExampleIndex, FeatureIndex));
}

int CDataSetForBinaryClassification::GetExampleLabel(int ExampleIndex)
{
	return (pRealDataSet==NULL ? -1 : pRealDataSet->GetExampleLabel(ExampleIndex));
}

void CDataSetForBinaryClassification::InitValidFeatures()
{
	if (pRealDataSet != NULL)
		pRealDataSet->InitValidFeatures();
}

int CDataSetForBinaryClassification::GetValidFeatureAmount()
{
	return (pRealDataSet==NULL ? 0 : pRealDataSet->GetValidFeatureAmount());
}

bool CDataSetForBinaryClassification::IsFeatureValid(int FeatureIndex)
{
	return (pRealDataSet==NULL ? false : pRealDataSet->IsFeatureValid(FeatureIndex));
}

void CDataSetForBinaryClassification::UpdateValidFeatures()
{
	if (pRealDataSet != NULL)
		pRealDataSet->UpdateValidFeatures();
}

CDataSetForBinaryClassification::CDataSetForBinaryClassification()
{
	SetRealDataSet(NULL);

	fSetPosIfNonNeg = false;
	LabelSet_Pos.clear(); LabelSet_Pos.insert(0);
	fSetNegIfNonPos = true;
	LabelSet_Neg.clear();
}

CDataSetForBinaryClassification::CDataSetForBinaryClassification(CLabeledDataSetForClassification* pFixedLabeledDataSetForClassification, int TargetLabel)
{
	SetRealDataSet(pFixedLabeledDataSetForClassification);

	SetTargetLabel(TargetLabel);
}

void CDataSetForBinaryClassification::SetRealDataSet(CLabeledDataSetForClassification* pFixedLabeledDataSetForClassification)
{
	this->pRealDataSet = pFixedLabeledDataSetForClassification;
}

void CDataSetForBinaryClassification::SetTargetLabel(int TargetLabel)
{
	CSimpleTypeArray<int> zz;
	zz.Clear(); zz.PushBack(TargetLabel);
	SetPosLabelSet(zz);
	SetNegLabelSetByNonPos();
}

void CDataSetForBinaryClassification::SetPosLabelSet(const CSimpleTypeArray<int>& PosLabelSet)
{
	//if (PosLabelSet.Size < 1)
	//{
	//	SetPosLabelSetByNonNeg();
	//	return;
	//}
	fSetPosIfNonNeg = false;
	LabelSet_Pos.clear();
	int i;
	for ( i = 0; i < (int)PosLabelSet.Size; ++i )
		LabelSet_Pos.insert(PosLabelSet.pElement[i]);
}

void CDataSetForBinaryClassification::SetNegLabelSet(const CSimpleTypeArray<int>& NegLabelSet)
{
	//if (NegLabelSet.Size < 1)
	//{
	//	SetNegLabelSetByNonPos();
	//	return;
	//}
	fSetNegIfNonPos = false;
	LabelSet_Neg.clear();
	int i;
	for ( i = 0; i < (int)NegLabelSet.Size; ++i )
		LabelSet_Neg.insert(NegLabelSet.pElement[i]);
}

void CDataSetForBinaryClassification::SetPosLabelSetByNonNeg()
{
	fSetPosIfNonNeg = true;
	LabelSet_Pos.clear();
}

void CDataSetForBinaryClassification::SetNegLabelSetByNonPos()
{
	fSetNegIfNonPos = true;
	LabelSet_Neg.clear();
}

void CDataSetForBinaryClassification::CopyLabelSettingFrom( const CDataSetForBinaryClassification& Another )
{
	this->fSetPosIfNonNeg = Another.fSetPosIfNonNeg;
	this->fSetNegIfNonPos = Another.fSetNegIfNonPos;
	this->LabelSet_Pos = Another.LabelSet_Pos;
	this->LabelSet_Neg = Another.LabelSet_Neg;
}

void CDataSetForBinaryClassification::InputLabelSettingsFromStream( istream& inStream )
{
	int i, ii, t;
	inStream>>fSetPosIfNonNeg>>ii;
	LabelSet_Pos.clear(); for ( i = 0; i < ii; ++i ) { inStream>>t; LabelSet_Pos.insert(t); }
	inStream>>fSetNegIfNonPos>>ii;
	LabelSet_Neg.clear(); for ( i = 0; i < ii; ++i ) { inStream>>t; LabelSet_Neg.insert(t); }
}

void CDataSetForBinaryClassification::OutputLabelSettingsToStream( ostream& outStream ) const
{
	set<int>::const_iterator itr;
	int ii;
	ii = (int)LabelSet_Pos.size(); outStream<<fSetPosIfNonNeg<<"\t"<<ii<<"\t";
	for ( itr = LabelSet_Pos.begin(); itr != LabelSet_Pos.end(); ++itr )
		outStream<<" "<<(*itr);
	outStream<<endl;
	ii = (int)LabelSet_Neg.size(); outStream<<fSetNegIfNonPos<<"\t"<<ii<<"\t";
	for ( itr = LabelSet_Neg.begin(); itr != LabelSet_Neg.end(); ++itr )
		outStream<<" "<<(*itr);
	outStream<<endl;
}

bool CDataSetForBinaryClassification::IsLabelPos( int Label )
{
	if (fSetPosIfNonNeg)
		return (LabelSet_Neg.find(Label) == LabelSet_Neg.end());
	else
		return (LabelSet_Pos.find(Label) != LabelSet_Pos.end());
}

bool CDataSetForBinaryClassification::IsLabelNeg( int Label )
{
	if (fSetNegIfNonPos)
		return (LabelSet_Pos.find(Label) == LabelSet_Pos.end());
	else
		return (LabelSet_Neg.find(Label) != LabelSet_Neg.end());
}

int CDataSetForBinaryClassification::GetLabelFlag( int Label )
{
	bool fP = (LabelSet_Pos.find(Label) != LabelSet_Pos.end());
	bool fN = (LabelSet_Neg.find(Label) != LabelSet_Neg.end());
	if (!fSetPosIfNonNeg && fP) return +1;
	if (fSetPosIfNonNeg && !fN) return +1;
	if (!fSetNegIfNonPos && fN) return -1;
	if (fSetNegIfNonPos && !fP) return -1;
	return 0;
}

bool CDataSetForBinaryClassification::IsExamplePos(int ExampleIndex)
{
	if (fSetPosIfNonNeg)
		return (LabelSet_Neg.find(GetExampleLabel(ExampleIndex)) == LabelSet_Neg.end());
	else
		return (LabelSet_Pos.find(GetExampleLabel(ExampleIndex)) != LabelSet_Pos.end());
}

bool CDataSetForBinaryClassification::IsExampleNeg(int ExampleIndex)
{
	if (fSetNegIfNonPos)
		return (LabelSet_Pos.find(GetExampleLabel(ExampleIndex)) == LabelSet_Pos.end());
	else
		return (LabelSet_Neg.find(GetExampleLabel(ExampleIndex)) != LabelSet_Neg.end());
}

int CDataSetForBinaryClassification::GetExampleFlag( int ExampleIndex )
{
	return GetLabelFlag(GetExampleLabel(ExampleIndex));
}

CBinaryClassificationResult_ByPrediction::CBinaryClassificationResult_ByPrediction()
{
}

CBinaryClassificationResult_ByPrediction::CBinaryClassificationResult_ByPrediction(int PosAmount, int NegAmount, int TruePosAmount, int FalsePosAmount)
{
	this->_PosAmount = PosAmount;
	this->_NegAmount = NegAmount;
	this->_TruePosAmount = TruePosAmount;
	this->_FalsePosAmount = FalsePosAmount;

	this->_PosWeight = PosAmount;
	this->_NegWeight = NegAmount;
	this->_TruePosWeight = TruePosAmount;
	this->_FalsePosWeight = FalsePosAmount;

	Analyse();
}

void CBinaryClassificationResult_ByPrediction::Clear()
{
	this->_PosAmount = 0;
	this->_NegAmount = 0;
	this->_TruePosAmount = 0;
	this->_FalsePosAmount = 0;

	this->_PosWeight = 0.0;
	this->_NegWeight = 0.0;
	this->_TruePosWeight = 0.0;
	this->_FalsePosWeight = 0.0;

	Analyse();
}

void CBinaryClassificationResult_ByPrediction::NewTest(int Label, int Prediction, double Weight)
{
	if (Label == +1)
	{
		++this->_PosAmount;
		this->_PosWeight += Weight;
		if (Prediction == +1)
		{
			++this->_TruePosAmount;
			this->_TruePosWeight += Weight;
		}
	}
	else
	{
		++this->_NegAmount;
		this->_NegWeight += Weight;
		if (Prediction == +1)
		{
			++this->_FalsePosAmount;
			this->_FalsePosWeight += Weight;
		}
	}
}

void CBinaryClassificationResult_ByPrediction::AddEquals(const CBinaryClassificationResult_ByPrediction& b)
{
	_PosAmount += b._PosAmount;
	_NegAmount += b._NegAmount;
	_TruePosAmount += b._TruePosAmount;
	_FalsePosAmount += b._FalsePosAmount;
	_PosWeight += b._PosWeight;
	_NegWeight += b._NegWeight;
	_TruePosWeight += b._TruePosWeight;
	_FalsePosWeight += b._FalsePosWeight;
	Analyse();
}

void CBinaryClassificationResult_ByPrediction::SubEquals(const CBinaryClassificationResult_ByPrediction& b)
{
	_PosAmount -= b._PosAmount;
	_NegAmount -= b._NegAmount;
	_TruePosAmount -= b._TruePosAmount;
	_FalsePosAmount -= b._FalsePosAmount;
	_PosWeight -= b._PosWeight;
	_NegWeight -= b._NegWeight;
	_TruePosWeight -= b._TruePosWeight;
	_FalsePosWeight -= b._FalsePosWeight;
	Analyse();
}

void CBinaryClassificationResult_ByPrediction::Analyse()
{
	if (_PosWeight+_NegWeight>0)
		wErrorRate = 1.0 * (_FalsePosWeight + _PosWeight - _TruePosWeight) / (_PosWeight + _NegWeight);
	else
		wErrorRate = 0.0;
	if (_PosWeight>0)
		wDetectionRate = 1.0 * (_TruePosWeight) / (_PosWeight);
	else
		wDetectionRate = 1.0;
	if (_NegWeight>0)
		wFalsePositiveRate = 1.0 * (_FalsePosWeight) / (_NegWeight);
	else
		wFalsePositiveRate = 0.0;
	if (_FalsePosWeight>0)
		wPrecision = 1.0 * (_TruePosWeight) / (_TruePosWeight + _FalsePosWeight);
	else
		wPrecision = 1.0;

	if (_PosAmount+_NegAmount>0)
		ErrorRate = 1.0 * (_FalsePosAmount + _PosAmount - _TruePosAmount) / (_PosAmount + _NegAmount);
	else
		ErrorRate = 0.0;
	if (_PosAmount>0)
		DetectionRate = 1.0 * (_TruePosAmount) / (_PosAmount);
	else
		DetectionRate = 1.0;
	if (_NegAmount>0)
		FalsePositiveRate = 1.0 * (_FalsePosAmount) / (_NegAmount);
	else
		FalsePositiveRate = 0.0;
	if (_FalsePosAmount>0)
		Precision = 1.0 * (_TruePosAmount) / (_TruePosAmount + _FalsePosAmount);
	else
		Precision = 1.0;
}

//void CBinaryClassificationResult_ByPrediction::InputFromStream(istream& inStream)
//{
//	char st[101];
//	inStream.getline(st,100,'{');
//
//	inStream.getline(st,100,'=');	inStream>>ErrorRate;
//	ErrorRate = (1.0 - ErrorRate)/100.0;
//	inStream.getline(st,100,'=');	inStream>>DetectionRate;
//	DetectionRate /= 100.0;
//	inStream.getline(st,100,'=');	inStream>>FalsePositiveRate;
//	FalsePositiveRate /= 100.0;
//	inStream.getline(st,100,'=');	inStream>>Precision;
//	Precision /= 100.0;
//
//	inStream.getline(st,100,'=');	inStream>>_TruePosAmount;
//	inStream.getline(st,100,'/');	inStream>>_PosAmount;
//	inStream.getline(st,100,'=');	inStream>>_FalsePosAmount;
//	inStream.getline(st,100,'/');	inStream>>_NegAmount;
//	_FalsePosAmount = _NegAmount - _FalsePosAmount;
//
//	inStream.getline(st,100,'}');
//}

void CBinaryClassificationResult_ByPrediction::OutputToStream_i(ostream& outStream)
{
	outStream.precision(2);

	outStream<<"{ ";
	outStream<<"Acc = "<<setw(6)<<fixed<<100.0*(1.0-ErrorRate);
	outStream<<", DR = "<<setw(6)<<fixed<<100.0*DetectionRate;
	outStream<<", FPR = "<<setw(6)<<fixed<<100.0*FalsePositiveRate;
	outStream<<", Prec = "<<setw(6)<<fixed<<100.0*Precision;
	outStream<<endl;
	outStream<<"  Pos = "<<setw(6)<<this->_TruePosAmount<<"/"<<setw(6)<<this->_PosAmount;
	outStream<<", Neg = "<<setw(6)<<this->_NegAmount-this->_FalsePosAmount<<"/"<<setw(6)<<this->_NegAmount;
	outStream<<" };"<<endl;
}

void CBinaryClassificationResult_ByPrediction::OutputToStream_w(ostream& outStream)
{
	outStream.precision(3);

	outStream<<"{ ";
	outStream<<"Acc = "<<setw(6)<<fixed<<100.0*(1.0-wErrorRate);
	outStream<<", DR = "<<setw(6)<<fixed<<100.0*wDetectionRate;
	outStream<<", FPR = "<<setw(6)<<fixed<<100.0*wFalsePositiveRate;
	outStream<<", Prec = "<<setw(6)<<fixed<<100.0*wPrecision;
	outStream<<endl;
	outStream<<"  Pos = "<<setw(6)<<this->_TruePosAmount<<"/"<<setw(6)<<this->_PosAmount;
	outStream<<", Neg = "<<setw(6)<<this->_NegAmount-this->_FalsePosAmount<<"/"<<setw(6)<<this->_NegAmount;
	outStream<<" };"<<endl;
}

CBinaryClassificationResult_ByConfidence::CBinaryClassificationResult_ByConfidence()
{
	Clear();
}

void CBinaryClassificationResult_ByConfidence::Clear()
{
	ExtraFalseNegAmount = 0;
	Pos.Clear();
	Neg.Clear();
}

void CBinaryClassificationResult_ByConfidence::Analyse()
{
	sort(Pos.pElement, Pos.pElement + Pos.Size);
	sort(Neg.pElement, Neg.pElement + Neg.Size);
}

int CBinaryClassificationResult_ByConfidence::GetTruePosAmount_By_Confidence(double ConfidenceThreshold)
{
	return (int)(Pos.Size - (upper_bound(Pos.Begin(), Pos.End(), ConfidenceThreshold) - Pos.Begin()));
}

int CBinaryClassificationResult_ByConfidence::GetFalsePosAmount_By_Confidence(double ConfidenceThreshold)
{
	return (int)(Neg.Size - (upper_bound(Neg.Begin(), Neg.End(), ConfidenceThreshold) - Neg.Begin()));
}

double CBinaryClassificationResult_ByConfidence::Get_ConfidenceThreshold_By_FalsePositiveRate(double FPR)
{
	double ret = 0.0;

	if (Neg.Size < 1) return ret;

	double tt = Neg.Size * FPR + EPS;
	if (tt<.1) tt = .1;
	if (tt>Neg.Size+.1) tt = Neg.Size+.1;
	int FPA = (int)tt;

	if (FPA == Neg.Size)
		ret = Neg.pElement[0] - EPS - EPS;
	else
		ret = Neg.pElement[Neg.Size - FPA - 1];

	return ret;
}

bool CBinaryClassificationResult_ByConfidence::Get_ConfidenceThreshold_By_Recall(double& dstConfidenceThreshold, double Recall)
{
	if (Recall > 1.0) Recall = 1.0;

	if (Pos.Size < 1) return 0.0;

	double tt = (Pos.Size + ExtraFalseNegAmount) * Recall + EPS;

	if (tt>Pos.Size+.1)
	{
		dstConfidenceThreshold = 0.0;
		return false;
	}

	if (tt<.1) tt = .1;
	if (tt>Pos.Size+.1) tt = Pos.Size+.1;
	int TPA = (int)tt;

	if (TPA == Pos.Size)
		dstConfidenceThreshold = Pos.pElement[0] - EPS - EPS;
	else
		dstConfidenceThreshold = Pos.pElement[Pos.Size - TPA - 1];

	return true;
}

double CBinaryClassificationResult_ByConfidence::Get_Precision_By_Confidence(double ConfidenceThreshold)
{
	int tpa = GetTruePosAmount_By_Confidence(ConfidenceThreshold);
	int fpa = GetFalsePosAmount_By_Confidence(ConfidenceThreshold);
	if (fpa > 0)
		return 1.0 * tpa / (tpa + fpa);
	else
		return 1.0;
}

double CBinaryClassificationResult_ByConfidence::Get_Precision_By_Recall(double Recall)
{
	double CT;
	if (Get_ConfidenceThreshold_By_Recall(CT, Recall))
		return Get_Precision_By_Confidence(CT);
	else
		return 0.0;
}

double CBinaryClassificationResult_ByConfidence::Get_Recall_By_Confidence(double ConfidenceThreshold)
{
	if (Pos.Size + ExtraFalseNegAmount > 0)
		return 1.0 * GetTruePosAmount_By_Confidence(ConfidenceThreshold) / (Pos.Size + ExtraFalseNegAmount);
	else
		return 1.0;
}

double CBinaryClassificationResult_ByConfidence::Get_Recall_By_FalsePositiveRate(double FPR)
{
	return Get_Recall_By_Confidence(Get_ConfidenceThreshold_By_FalsePositiveRate(FPR));
}

double CBinaryClassificationResult_ByConfidence::Get_AveragePrecision()
{
	double ret = 0.0;

	double ConfidenceThreshold;
	int iP, iN;

	ret = 1.0;
	for ( iN = (int)Neg.Size, iP = 1; iP <= (int)Pos.Size; ++iP )
	{
		//cout<<"\rCalculating AvgPrec: "<<iP<<" / "<<Pos.Size<<", AP = "<<ret/(1.0+iP);
		ConfidenceThreshold = Pos.pElement[Pos.Size - iP] - EPS;
		while (iN>0 && Neg.pElement[iN-1]>ConfidenceThreshold)
			--iN;
		ret += 1.0 * iP / (iP + Neg.Size - iN);
	}
	//CExternalTools::ConsoleTools::ClearCurrentLine();
	ret /= (Pos.Size + ExtraFalseNegAmount + 1.0);

	return ret;
}

void CBinaryClassificationResult_ByConfidence::OutputPRCtoStream(ostream& outStream, int Length, double RecallLow, double RecallHigh, bool fUseLogScale)
{
	int i;
	double p,r,ct;
	double la,lb,lt;

	Analyse();

	outStream.precision(8);
	outStream<<Length<<"\t"<<fixed<<RecallLow<<"\t"<<fixed<<RecallHigh<<endl;
	if (fUseLogScale)
	{
		la = log(RecallLow);
		lb = log(RecallHigh);
	}
	else
	{
		la = RecallLow;
		lb = RecallHigh;
	}
	for ( i = 0; i <= Length; ++i )
	{
		lt = la + (lb - la) * i / Length;
		if (fUseLogScale)
			r = exp(lt);
		else
			r = lt;
		if (Get_ConfidenceThreshold_By_Recall(ct, r))
			p = Get_Precision_By_Confidence(ct);
		else
		{
			ct = 0.0;
			p = 0.0;
		}
		outStream<<fixed<<r<<"\t"<<fixed<<p<<"\t"<<fixed<<ct<<endl;
	}
}

void CBinaryClassificationResult_ByConfidence::OutputROCtoStream(ostream& outStream, int Length, double FPRLow, double FPRHigh, bool fUseLogScale)
{
	int i;
	double fpr,r,ct;
	double la,lb,lt;

	Analyse();

	outStream.precision(8);
	outStream<<Length<<"\t"<<fixed<<FPRLow<<"\t"<<fixed<<FPRHigh<<endl;
	if (fUseLogScale)
	{
		la = log(FPRLow);
		lb = log(FPRHigh);
	}
	else
	{
		la = FPRLow;
		lb = FPRHigh;
	}
	for ( i = 0; i <= Length; ++i )
	{
		lt = la + (lb - la) * i / Length;
		if (fUseLogScale)
			fpr = exp(lt);
		else
			fpr = lt;
		ct = Get_ConfidenceThreshold_By_FalsePositiveRate(fpr);
		r = Get_Recall_By_Confidence(ct);
		//outStream<<fixed<<log(fpr)<<"\t"<<fixed<<log(r)<<endl;
		outStream<<fixed<<fpr<<"\t"<<fixed<<r<<"\t"<<fixed<<ct<<"\t"<<fixed<<1.0-r<<endl;
	}
}

// [*] CBinaryClassificationExampleList

CBinaryClassificationExampleList::CBinaryClassificationExampleList()
{
	Clear();
}

void CBinaryClassificationExampleList::Clear()
{
	PosList.Clear();
	NegList.Clear();
}

void CBinaryClassificationExampleList::myInit(CDataSetForBinaryClassification* pDataSet)
{
	int i,na;
	CWeightedClassificationExample tn;

	Clear();
	na = pDataSet->GetExampleAmount();
	for ( i = 0; i < na; ++i )
	{
		tn.ExampleIndex = i;
		tn.Weight = pDataSet->GetExampleInitialWeight(i);
		if (pDataSet->IsExamplePos(i))
			PosList.PushBack(tn);
		if (pDataSet->IsExampleNeg(i))
			NegList.PushBack(tn);
	}
	this->ComputeTotalExampleWeight();
}

void CBinaryClassificationExampleList::myInit(CDataSetForBinaryClassification* pDataSet, const CWeightedClassificationExampleList& ExampleList)
{
	int i,na;
	CWeightedClassificationExample tn;

	Clear();
	na = ExampleList.ExampleAmount;
	for ( i = 0; i < na; ++i )
	{
		tn = ExampleList.rgExample[i];
		if (pDataSet->IsExamplePos(tn.ExampleIndex))
			PosList.PushBack(tn);
		if (pDataSet->IsExampleNeg(tn.ExampleIndex))
			NegList.PushBack(tn);
	}
	this->ComputeTotalExampleWeight();
}

void CBinaryClassificationExampleList::myInit(const CBinaryClassificationExampleList& Src, double SampleRate)
{
	myInit(Src, SampleRate, SampleRate);
}

void CBinaryClassificationExampleList::myInit(const CBinaryClassificationExampleList& Src, double PosSampleRate, double NegSampleRate)
{
	PosList.myInit(Src.PosList, PosSampleRate);
	NegList.myInit(Src.NegList, NegSampleRate);

	//int i, pa, na;
	//pa = (int)(Src.PosList.ExampleAmount * PosSampleRate + 0.5);
	//if (pa < 0) pa = 0;
	//if (pa > Src.PosList.ExampleAmount) pa = Src.PosList.ExampleAmount;
	//na = (int)(Src.NegList.ExampleAmount * NegSampleRate + 0.5);
	//if (na < 0) na = 0;
	//if (na > Src.NegList.ExampleAmount) na = Src.NegList.ExampleAmount;
	//Clear();
	//for ( i = 0; i < Src.PosList.ExampleAmount; ++i )
	//{
	//	if (CMyRandom::NextInt(Src.PosList.ExampleAmount-i) >= pa) continue;
	//	PosList.PushBack(Src.PosList.rgExample[i]);
	//	--pa;
	//}
	//for ( i = 0; i < Src.NegList.ExampleAmount; ++i )
	//{
	//	if (CMyRandom::NextInt(Src.NegList.ExampleAmount-i) >= na) continue;
	//	NegList.PushBack(Src.NegList.rgExample[i]);
	//	--na;
	//}
	//this->ComputeTotalExampleWeight();
}

void CBinaryClassificationExampleList::SortByIndex()
{
	PosList.SortByIndex();
	NegList.SortByIndex();
}

void CBinaryClassificationExampleList::SortByWeight()
{
	PosList.SortByWeight();
	NegList.SortByWeight();
}

void CBinaryClassificationExampleList::MakeMixedList(CWeightedClassificationExampleList& DstExampleList) const
{
	int i;

	DstExampleList.Clear();
	for ( i = 0; i < PosList.ExampleAmount; ++i )
		DstExampleList.PushBack(PosList.rgExample[i]);
	for ( i = 0; i < NegList.ExampleAmount; ++i )
		DstExampleList.PushBack(NegList.rgExample[i]);
	DstExampleList.ComputeTotalExampleWeight();
}

int CBinaryClassificationExampleList::GetTotalExampleAmount() const
{
	return PosList.ExampleAmount + NegList.ExampleAmount;
}

double CBinaryClassificationExampleList::GetTotalExampleWeight() const
{
	return PosList.TotalExampleWeight + NegList.TotalExampleWeight;
}

void CBinaryClassificationExampleList::ComputeTotalExampleWeight()
{
	PosList.ComputeTotalExampleWeight();
	NegList.ComputeTotalExampleWeight();
}

void CBinaryClassificationExampleList::NormalizeExampleWeight(double TargetTotalExampleWeight)
{
	int i;
	double t;

	ComputeTotalExampleWeight();
	t = GetTotalExampleWeight() / TargetTotalExampleWeight;
	if (t > MINIMUM_DOUBLE_EPS_C)
	{
		PosList.TotalExampleWeight /= t;
		for ( i = 0; i < PosList.ExampleAmount; ++i )
			PosList.rgExample[i].Weight /= t;
		NegList.TotalExampleWeight /= t;
		for ( i = 0; i < NegList.ExampleAmount; ++i )
			NegList.rgExample[i].Weight /= t;
	}
}

void CBinaryClassificationExampleList::BalanceRelativePosNegTotalWeightRatio( double RelativePosNegWeightRatio )
{
	if (RelativePosNegWeightRatio < MINIMUM_DOUBLE_EPS_C) return;
	this->ComputeTotalExampleWeight();
	double x = PosList.TotalExampleWeight;
	double y = NegList.TotalExampleWeight;
	if (x < MINIMUM_DOUBLE_EPS_C) return;
	if (y < MINIMUM_DOUBLE_EPS_C) return;
	double wp, wn;
	wp = (x+y) / x / (RelativePosNegWeightRatio + 1.0) * RelativePosNegWeightRatio;
	wn = (x+y) / y / (RelativePosNegWeightRatio + 1.0) * 1.0;
	PosList.MultiplyEachWeightBy(wp);
	NegList.MultiplyEachWeightBy(wn);
}

// [*] CBinaryClassifier

void CBinaryClassifier::SaveToFile(string strFN_dstModel)
{
	ofstream outFile(strFN_dstModel.c_str());
	OutputToStream(outFile);
	outFile.clear();
	outFile.close();
}

bool CBinaryClassifier::LoadFromFile(string strFN_srcModel)
{
	ifstream inFile(strFN_srcModel.c_str());
	bool ret = InputFromStream(inFile);
	inFile.clear();
	inFile.close();
	return ret;
}

void CBinaryClassifier::Classify(CDataSetForClassification* pDataSet, int ExampleIndex, double& DstConfidence, int& DstPrediction)
{
	_Classify(pDataSet, ExampleIndex, DstConfidence, DstPrediction);
}

void CBinaryClassifier::Classify(CSingleDataForClassification* pSingleData, double& DstConfidence, int& DstPrediction)
{
	_Classify(pSingleData, 0, DstConfidence, DstPrediction);
}

double CBinaryClassifier::Classify(CDataSetForClassification* pDataSet, int ExampleIndex)
{
	double ret;
	int tp;
	_Classify(pDataSet, ExampleIndex, ret, tp);
	return ret;
}

double CBinaryClassifier::Classify(CSingleDataForClassification* pSingleData)
{
	return Classify(pSingleData, 0);
}

void CBinaryClassifier::Classify(
	CDataSetForClassification* pDataSet, CBinaryClassificationExampleList& BinaryExampleList,
	CBinaryClassificationResult_ByConfidence& DstResult_ByConfidence,
	CBinaryClassificationResult_ByPrediction& DstResult_ByPrediction )
{
	int i_example, tP;
	double tC;
	DstResult_ByConfidence.Clear();
	DstResult_ByPrediction.Clear();
	for ( i_example = 0; i_example < BinaryExampleList.PosList.ExampleAmount; ++i_example )
	{
		const CWeightedClassificationExample& te = BinaryExampleList.PosList.rgExample[i_example];
		this->_Classify(pDataSet, te.ExampleIndex, tC, tP);
		DstResult_ByConfidence.Pos.PushBack(tC);
		DstResult_ByPrediction.NewTest(+1, tP, te.Weight);
	}
	for ( i_example = 0; i_example < BinaryExampleList.NegList.ExampleAmount; ++i_example )
	{
		const CWeightedClassificationExample& te = BinaryExampleList.NegList.rgExample[i_example];
		this->_Classify(pDataSet, te.ExampleIndex, tC, tP);
		DstResult_ByConfidence.Neg.PushBack(tC);
		DstResult_ByPrediction.NewTest(-1, tP, te.Weight);
	}
}

void CBinaryClassifier::Classify(
	CDataSetForBinaryClassification* pBinaryDataSet, CWeightedClassificationExampleList& ExampleList,
	CBinaryClassificationResult_ByConfidence& DstResult_ByConfidence,
	CBinaryClassificationResult_ByPrediction& DstResult_ByPrediction )
{
	int i_example, tP, tF;
	double tC;
	DstResult_ByConfidence.Clear();
	DstResult_ByPrediction.Clear();
	for ( i_example = 0; i_example < ExampleList.ExampleAmount; ++i_example )
	{
		const CWeightedClassificationExample& te = ExampleList.rgExample[i_example];
		tF = pBinaryDataSet->GetExampleFlag(te.ExampleIndex);
		if (tF == 0) continue;
		this->_Classify(pBinaryDataSet->pRealDataSet, te.ExampleIndex, tC, tP);
		if (tF == +1)
		{
			DstResult_ByConfidence.Pos.PushBack(tC);
			DstResult_ByPrediction.NewTest(+1, tP, te.Weight);
		}
		else
		{
			DstResult_ByConfidence.Neg.PushBack(tC);
			DstResult_ByPrediction.NewTest(-1, tP, te.Weight);
		}
	}
}

